<template>
  <div class="board-container">
    <div :class="{
    stop: gameStatus === 2
    }">
      <slot :gameStatus="gameStatus" :gameResult="gameResult"></slot>
    </div>

    <div class="main-board"
      @click="evaluating"
      @contextmenu.prevent="testing"
    >
      <div v-for="(vals, i) in gameBoard" :key="i" class="row">

        <!-- key发生了重复。痛苦的故事！ -->
        <Block v-for="(item, j) in vals" 
          :key="item.d" 
          :blockItem="item"
          :data-row="i"
          :data-col="j"
          ref="blks"
          ></Block>
      </div>
    </div>
  </div>
</template>

<script>
import Block from "./block.vue";
import BlockType from './block.js'
import shuffle from '@/utils/shuffle'
import { defineComponent, reactive, ref } from 'vue';


export default defineComponent({
  components: {
    Block
  },

  setup() {

    /*
      init board
    */
    let bombsCoords = []
    let blkCounter = 0
    let gMines = 1
    let flagCounter = 0
    const flaged = {}
    const searched = new Set()
    const gRows= ref(2)
    const gCols= ref(2)
    const gameBoard= ref([])
    const gameStatus= ref(0)
    const gameResult= ref(false)

    /*
      reset board
    */
    const resetBoard = () => {
      gMines = 1
      blkCounter = 0
      bombsCoords = []
      flagCounter = 0
      Object.assign(flaged, {})
      searched.clear()
      gRows.value = 2 
      gCols.value = 2 
      gameBoard.value = []
      gameStatus.value = 0 
      gameResult.value = false  
    }

    const shuffleBlks = (dir) => {
      // dir 1 - row 2 - col
      const bombCoor = []
      if(dir === 1) {
        for(let i=0;i<gRows.value;i+=1){
          for(let j=gCols.value - 1;j>=0;j-=1){
            const next = shuffle.getRandInt(0, j);
            const currTarg = gameBoard.value[i][j]
            currTarg.swapItem(gameBoard.value[i][next])
            if(currTarg.isMine()) {
              bombCoor.push([i, j])
            }
          }
        }
      } else {
        for(let i=0;i<gCols.value;i+=1){
          for(let j=gRows.value -1; j>=0;j-=1) {
            const next = shuffle.getRandInt(0, j);
            const currTarg = gameBoard.value[j][i]
            currTarg.swapItem(gameBoard.value[next][i])
            if(currTarg.isMine()) {
              bombCoor.push([j, i])
            }
          }
        }
      }

      return bombCoor
    }

      // x -> rows y -> cols
    const getRanges = ([x, y]) => [x-1 < 0 ? 0 : x-1, y-1<0?0:y-1, x+1 === gRows.value ? x : x+1, y+1===gCols.value?y:y+1]
    
    const numberCalc = () => {
      for(let bomb of bombsCoords) {
        const [x, y] = bomb
        const [upperX, upperY, lowerX, lowerY] = getRanges(bomb)
        for(let i=upperX; i<=lowerX; i+=1){
          for(let j=upperY; j <= lowerY; j += 1) {
            if(i===x && j === y) {
              continue
            }
            const currTarg = gameBoard.value[i][j]
            if (currTarg.item === -1) {
              continue
            }
            currTarg.item += 1
          }
        }
      }
    }

    const dd2d = ([x, y]) => x * gCols.value + y
    const d2dd = (d) => {
      const y = d % gCols.value
      const x = (d - y) / gCols.value
      return [x, y]
    }

    const initVals = (r, c, m) => {
      gRows.value = r
      gCols.value = c
      gMines = m
      blkCounter = r * c
    }

    const fillMines = () => {
      for(let i=0;i<gRows.value;i+=1){
        const newRow = new Array(gCols.value)
        for(let j=0;j<gCols.value;j+=1){
          const tmpBlk = reactive(new BlockType(i, j, dd2d([i, j])))
          if(gMines) {
            tmpBlk.item = -1
            gMines -= 1
          }
          newRow[j] = tmpBlk
        }
        gameBoard.value.push(newRow)
      }
    }

    const generateBoard = ({rows, cols, mines}) => {
      initVals(rows, cols, mines)
      fillMines()
      shuffleBlks(1)
      bombsCoords = shuffleBlks(2)
      numberCalc()
      bombsCoords = bombsCoords.map(e => dd2d(e))
      gameStatus.value = 1
    }

    /*
      left click
    */
    const revealMines = () => {
      bombsCoords.forEach(e => {
        const [x, y] = d2dd(e)
        const currTarg = gameBoard.value[x][y]
        currTarg.lClick()
      })
    }

    const end = (state) => {
      gameStatus.value = 2
      gameResult.value = state
      if(!state){
        revealMines()
      }
    }

    const move4 = ([x, y]) => {
      const [upperX, upperY, lowerX, lowerY] = getRanges([x, y])
      // 最笨但是最有效率的办法
      const res = []
      if(upperX !== x) {
        res.push([upperX, y])
      }
      if(lowerX !== x) {
        res.push([lowerX, y])
      }
      if(upperY !== y) {
        res.push([x, upperY])
      }
      if(lowerY !== y) {
        res.push([x, lowerY])
      }
      return res
    }

    const winMines = () => {
      if(blkCounter === bombsCoords.length && gameStatus.value === 1) {
        end(true)
      }
    }

    const evaluating = (e) => {
      const { row, col } = e.target.dataset
      const coord = [row, col].map(e => e>>>0)
      if(gameStatus.value !== 1) return
      const protoTarget = gameBoard.value[row][col]
      if(!protoTarget.lAvail() || protoTarget.isTriggered()) return
      protoTarget.userTriggered()
      const tasks = []
      tasks.push(coord)
      // const searched = new Set() // 这玩意儿应该全局共享 不应该丢在这里
      while(tasks.length) {
        const curr = tasks.shift()
        const [cx, cy] = curr
        const currTarg = gameBoard.value[cx][cy]
        if(currTarg.sign){
          continue
        }
        const currPos = currTarg.d
        if(searched.has(currPos)){
          continue
        }
        const currItem = currTarg.lClick()
        blkCounter -= 1
        searched.add(currPos)
        // move4的条件 多少复杂
        if(currItem === -1) {
          end(false)
        }

        if(currItem === 0){
          tasks.push(...move4(curr))
        }
      }
      winMines()
    }

    /*
      right click
    */
    const checkFlags = () => {
      if(flagCounter === bombsCoords.length && bombsCoords.every(e => flaged[e])) {
        end(true)
      }
    }

    const testing = (e) => {
      if(gameStatus.value !== 1) return
      const { row, col } = e.target.dataset
      const target = gameBoard.value[row][col]
      if(!target.rAvail()) return
      const [oldState, newState, d] = target.rClick()
      // 增加的情况
      if(newState === 1) {
        flaged[d] = true
        flagCounter += 1
        checkFlags()
      } else if(oldState === 1) {
        // 减少的情况
        delete flaged[d]
        flagCounter -= 1
        checkFlags()
      }
    }

    return {
      gameStatus,
      gameResult,
      gameBoard,
      evaluating,
      testing,
      generateBoard,
      resetBoard
    }
  }
})

</script>
<style scoped>
.row {
  display: flex;
}
.board-container {
  overflow: hidden;
  position: relative;
}
.stop {
  position: absolute;
  background: rgba(0, 0, 0, .5);
  width: 100%;
  height: 100%;
  z-index: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #FFF;
}
</style>